﻿using System; 
using System.Text;

using System.Security.Cryptography;
using Gama.Contract;

namespace Gama.Service
{
    public class SecurityService : ISecurityService
    {
        public const string AuthenticationType = "Application";

        static SecurityService()
        {
            instance = new SecurityService();
        }

        private static readonly SecurityService instance;

        public static SecurityService Instance { get { return instance; } }

        [System.Diagnostics.Conditional("DEBUG")]
        public void DemandIdentity(string identityName)
        {
            //#if !DEBUG
            //            var principal = Thread.CurrentPrincipal;

            //            if (principal != null)
            //            {
            //                var identity = principal.Identity;

            //                if (identity != null)
            //                {
            //                    if (identity.Name == identityName)
            //                    {
            //                        return;
            //                    }
            //                }
            //            }

            //            throw new SecurityAccessDeniedException(string.Format("Identity {0} is demanded", identityName));
            //#endif
        }

        public bool ValidateGuest(Guid id)
        {
            var service = new AccountService();
            var guest = service.GetGuest(id);
            if (guest == null)
                return false;
            return service.GetGuest(id).Id != default(Guid);
        }
        
        public Guid CreateGuest()
        {
            var service = new AccountService();
            var guest = service.AddGuest();
            return guest.Id;
        }

        #region Password Encryption

        /// <summary>
        /// Obtain the random number (salt) from the password store.
        /// Add the random number to the password string.
        /// Compute the hash, or encrypt the resulting string.
        /// Compare the result with the stored hash or encrypted password, a match indicates a password match.
        /// </summary>
        /// <param name="password"></param>
        /// <param name="dbPassword"></param>
        /// <returns></returns>
        public virtual bool ComparePassword(string password, string dbPassword)
        {
            string[] sub = dbPassword.Split(':');
            string passPart = sub[0];
            string saltPart = sub[1];

            string encMergedPass = this.Encrypt(saltPart, password);

            return encMergedPass.Equals(passPart);
        }

        public string Encrypt(string password)
        {
            //Create salt
            string salt = this.GenerateRandomString(2, CharSets.Alphanumeric);

            //Encrypt
            string encMergedPass = this.Encrypt(salt, password);

            return string.Format("{0}:{1}", encMergedPass, salt);
        }

        protected virtual string Encrypt(string salt, string password)
        {
            //Merge
            string mergedPass = string.Format("{0}{1}", salt, password);

            //Hash
            return this.Hash(mergedPass);
        }

        protected virtual string Hash(string input)
        {
            byte[] originalBytes = Encoding.UTF8.GetBytes(input);

            MD5 encryptType = new MD5CryptoServiceProvider();

            byte[] encodedBytes = encryptType.ComputeHash(originalBytes);

            return BitConverter.ToString(encodedBytes).Replace("-", "").ToLower();
        }

        public string GenerateRandomString(int length, Contract.CharSets charSet)
        {
            return GenerateRandomString(length, (CharSets)Enum.Parse(typeof(CharSets), charSet.ToString(), true));
        }

        public virtual string GenerateRandomString(int length, CharSets charSet)
        {
            switch (charSet)
            {
                case CharSets.Letters:
                    return GenerateRandomString(length, "abcdefghijklmnopqrstvwxyz");

                case CharSets.Numerics:
                    return GenerateRandomString(length, "0123456789");

                case CharSets.Alphanumeric:
                    return GenerateRandomString(length, "abcdefghijklmnopqrstvwxyz0123456789");

                default:
                    break;
            }

            return String.Empty;
        }

        protected virtual string GenerateRandomString(int length, string charSet)
        {
            return GenerateRandomString(length, charSet.ToCharArray());
        }

        protected virtual string GenerateRandomString(int length, params char[] charSet)
        {
            Random random = new Random();
            string randomString = string.Empty;
            for (int i = 0; i < length; i++)
            {
                int index = random.Next(charSet.Length - 1);

                randomString += charSet[index];
            }

            return randomString;
        }

        #endregion

        
    }

    public enum CharSets
    {
        Letters,
        Numerics,
        Alphanumeric
    }
}
